package com.aeye.mbr.common.react;

import com.aeye.mbr.common.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * React拦截器
 * Created by chengshengru on 2017/2/17.
 */
public class ReactInterceptor extends HandlerInterceptorAdapter {


    private final static Logger log = LoggerFactory.getLogger(ReactInterceptor.class);


    private static Pattern CAMEL_PATTERN = Pattern.compile("[A-Z]");


    private String prefix = "";

    private String configLocation = ReactConstant.DEFAULT_CONFIG_FILE_NAME;


    private String defaultViewName = ReactConstant.DEFAULT_TEMPLATE_VIEW_NAME;

    private String mHash = null;


    private String mPublicPath = null;


    private String mCssName = null;

    private String mChunkName = null;

    private Map<String, String> mJsLibs = new HashMap<>();

    private Map<String, String> mCssLibs = new HashMap<>();


    public ReactInterceptor() {
        readReactConfigFile();
    }

    /**
     * 读取react配置文件
     */
    public void readReactConfigFile() {
        BufferedReader reader = null;
        try {
            StringBuilder builder = new StringBuilder();
            if(StringUtils.isNotEmpty(prefix)){
                builder.append(prefix);
                builder.append("/");
            }
            builder.append(configLocation);
            ClassPathResource classPathResource = new ClassPathResource(builder.toString());
            reader = new BufferedReader(new InputStreamReader(classPathResource.getInputStream(), "utf-8"));
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            JSONObject config = JSON.parseObject(sb.toString());
            mPublicPath = config.getString("publicPath");
            mHash = config.getString("hash");
            mCssName = config.getString("cssName");
            mChunkName = config.getString("chunkName");
            JSONArray libs = config.getJSONArray("libs");
            if (libs != null && libs.size() > 0) {
                int length = libs.size();
                JSONObject item = null;
                String type = null;
                for (int i = 0; i < length; i++) {
                    item = libs.getJSONObject(i);
                    type = item.getString("type");
                    if ("js".equalsIgnoreCase(type)) {
                        mJsLibs.put(item.getString("name"), item.getString("path"));
                    } else if ("css".equalsIgnoreCase(type)) {
                        mCssLibs.put(item.getString("name"), item.getString("path"));
                    }
                }
            }
        } catch (Exception e) {
            log.error("读取配置文件失败!", e);
        } finally {
            IOUtils.closeQuietly(reader);
        }

    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;
            Method method = hm.getMethod();
            React react = method.getAnnotation(React.class);
            if (react != null) {

                String[] value = react.value();
                if (value.length == 0) {
                    try {
                        value = new String[1];
                        value[0] = camelToHyphen(method.getName(), "-");
                    } catch (Exception e) {

                    }
                }
                String path = mPublicPath;
                if (StringUtils.isNotBlank(react.publicPath())) {
                    path = react.publicPath();
                }
                if (path == null) {
                    path = "";
                }
                String hash = "";
                if (StringUtils.isNotBlank(mHash)) {
                    hash = "." + mHash;
                }
                List<String> reactFilePaths = new ArrayList<String>();
                String[] exclude = react.exclude();
                Set<String> excludeLib = new HashSet<String>();
                if (exclude != null) {
                    for (String key : exclude) {
                        excludeLib.add(key);
                    }
                }
                if (!CollectionUtils.isEmpty(mJsLibs)) {
                    for (Map.Entry<String, String> entry : mJsLibs.entrySet()) {
                        if (!excludeLib.contains(entry.getKey())) {
                            reactFilePaths.add(entry.getValue());
                        }
                    }
                }
                if (StringUtils.isNotBlank(mChunkName)) {
                    reactFilePaths.add((path + "/" + mChunkName + hash + ".js").replace("//", "/"));
                }
                for (String jsName : value) {
                    reactFilePaths.add((path + "/" + jsName + hash + ".js").replace("//", "/"));
                }
                request.setAttribute(ReactConstant.KEY_REACT_JS_FILES, reactFilePaths);

                reactFilePaths = new ArrayList<String>();

                if (!CollectionUtils.isEmpty(mCssLibs)) {
                    for (Map.Entry<String, String> entry : mCssLibs.entrySet()) {
                        if (!excludeLib.contains(entry.getKey())) {
                            reactFilePaths.add(entry.getValue());
                        }
                    }
                }
                if (StringUtils.isNotBlank(mCssName)) {
                    reactFilePaths.add((path + "/" + mCssName + hash + ".css").replace("//", "/"));
                }
                String[] css = react.css();
                if (css != null && css.length > 0) {
                    for (String cssName : css) {
                        reactFilePaths.add((path + "/" + cssName + hash + ".css").replace("//", "/"));
                    }
                }
                request.setAttribute(ReactConstant.KEY_REACT_CSS_FILES, reactFilePaths);
                String title = null;
                if (modelAndView != null) {
                    Map<String, Object> model = modelAndView.getModel();
                    if (model != null) {
                        title = (String) model.get(ReactConstant.KEY_REACT_TITLE);
                    }
                    Object defaultState = modelAndView.getModel().get(ReactConstant.KEY_REACT_DEFAULT_STATE);
                    if (defaultState == null) {
                        modelAndView.addObject(ReactConstant.KEY_REACT_DEFAULT_STATE, "{}");
                    } else {
                        modelAndView.addObject(ReactConstant.KEY_REACT_DEFAULT_STATE, JSON.toJSONString(defaultState));
                    }
                    String view = modelAndView.getViewName();
                    if (StringUtils.isBlank(view)) {
                        modelAndView.setViewName(defaultViewName);
                    }
                }
                if (title == null) {
                    title = react.title();
                }
                request.setAttribute(ReactConstant.KEY_REACT_TITLE, title);
                request.setAttribute(ReactConstant.KEY_REACT_DEBUG, react.debug());

            }
            super.postHandle(request, response, handler, modelAndView);
        }
    }

    /**
     * 驼峰转-
     */
    public static String camelToHyphen(String str, String split) {
        if (split == null) {
            split = "_";
        }
        Matcher matcher = CAMEL_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, split + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


    public void setConfigLocation(String fileName) {
        this.configLocation = fileName;
    }


    public void setDefaultViewName(String viewName) {
        this.defaultViewName = viewName;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
}
